using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Mail;
using System.Web.Services;
using newtelligence.DasBlog.Runtime;
using newtelligence.DasBlog.Web.Core;

namespace newtelligence.DasBlog.Web.Services
{
	/// <summary>
	/// Summary description for DasBlogEditting.
	/// </summary>
	public class EditServiceImplementation : WebService
	{
		public EditServiceImplementation()
		{
			InitializeComponent();
		}

		#region Component Designer generated code

		//Required by the Web Services Designer 
		private IContainer components = null;

		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose(bool disposing)
		{
			if (disposing && components != null)
			{
				components.Dispose();
			}
			base.Dispose(disposing);
		}

		#endregion

		[WebMethod]
		public bool CanEdit(string username, string password)
		{
			if (!SiteConfig.GetSiteConfig().EnableEditService)
			{
				throw new ServiceDisabledException();
			}

			try
			{
				if (HttpContext.Current.Request.IsAuthenticated)
				{
					UserToken token = SiteSecurity.GetToken(User.Identity.Name);
					if (token.Role == "admin") return true;
				}

				return SiteSecurity.Login(username, password).Role == "admin";
			}
			catch (Exception e)
			{
				ErrorTrace.Trace(TraceLevel.Error, e);
				return false;
			}
		}

		[WebMethod]
		public void DeleteEntry(string entryId, string username, string password)
		{
			SiteConfig siteConfig = SiteConfig.GetSiteConfig();
			if (!siteConfig.EnableEditService)
			{
				throw new ServiceDisabledException();
			}

			if (SiteSecurity.Login(username, password).Role != "admin")
			{
				throw new Exception("Invalid Password");
			}

			ILoggingDataService logService = LoggingDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.LogDir));
			IBlogDataService dataService = BlogDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.ContentDir), logService);

			//dataService.DeleteEntry(entryId, siteConfig.CrosspostSites);
			DeleteEntry(entryId, siteConfig, logService, dataService);
		}

		[WebMethod]
		public string UpdateEntry(Entry entry, string username, string password)
		{
			SiteConfig siteConfig = SiteConfig.GetSiteConfig();
			if (!siteConfig.EnableEditService)
			{
				throw new ServiceDisabledException();
			}

			if (SiteSecurity.Login(username, password).Role != "admin")
			{
				throw new Exception("Invalid Password");
			}

			ILoggingDataService logService = LoggingDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.LogDir));
			IBlogDataService dataService = BlogDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.ContentDir), logService);

			UpdateEntry(entry, "", null, siteConfig, logService, dataService);

			return "not found";
		}

		[WebMethod]
		public string CreateEntry(Entry entry, string username, string password)
		{
			SiteConfig siteConfig = SiteConfig.GetSiteConfig();
			if (!siteConfig.EnableEditService)
			{
				throw new ServiceDisabledException();
			}

			if (SiteSecurity.Login(username, password).Role != "admin")
			{
				throw new Exception("Invalid Password");
			}

			// ensure that the entryId was filled in
			//
			if (entry.EntryId == null || entry.EntryId.Length == 0)
			{
				entry.EntryId = Guid.NewGuid().ToString();
			}

			ILoggingDataService logService = LoggingDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.LogDir));
			IBlogDataService dataService = BlogDataServiceFactory.GetService(Context.Server.MapPath(siteConfig.ContentDir), logService);

			SaveEntry(entry, "", null, siteConfig, logService, dataService);

			return entry.EntryId;
		}

		public static void DeleteEntry(string entryId, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			try
			{
				Entry entry = dataService.GetEntry(entryId);
				string permalink = Utils.GetPermaLinkUrl(entry.EntryId);
				//string[] categories = entry.GetSplitCategories();

				dataService.DeleteEntry(entryId, siteConfig.CrosspostSites);

				BreakCache(siteConfig, entry.GetSplitCategories());

				// give the XSS upstreamer a hint that things have changed
				XSSUpstreamer.TriggerUpstreaming();

				// TODO: when we add support for more than just enclosures, we can't delete the entire folder
				DirectoryInfo enclosuresPath = new DirectoryInfo((Path.Combine(SiteConfig.GetBinariesPathFromCurrentContext(), entryId)));
				if (enclosuresPath.Exists) enclosuresPath.Delete(true);


				logService.AddEvent(
					new EventDataItem(
					EventCodes.EntryDeleted, entry.Title,
					permalink));
			}
			catch (Exception ex)
			{
				StackTrace st = new StackTrace();
				logService.AddEvent(
					new EventDataItem(EventCodes.Error, ex.ToString() + Environment.NewLine + st.ToString(), HttpContext.Current.Request.RawUrl));
			}
		}

		public static void SaveEntry(Entry entry, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			SaveEntry(entry, null, null, siteConfig, logService, dataService);
		}

		public static void SaveEntry(Entry entry, CrosspostInfoCollection crosspostList, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			SaveEntry(entry, null, crosspostList, siteConfig, logService, dataService);
		}

		public static void SaveEntry(Entry entry, string trackBackText, CrosspostInfoCollection crosspostList, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			InternalSaveEntry(entry, trackBackText, crosspostList, siteConfig, logService, dataService);

			logService.AddEvent(
				new EventDataItem(
					EventCodes.EntryAdded, entry.Title,
					Utils.GetPermaLinkUrl( siteConfig, entry.EntryId)));
		}

		public static void UpdateEntry(Entry entry, string trackBackText, CrosspostInfoCollection crosspostList, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			InternalSaveEntry(entry, trackBackText, crosspostList, siteConfig, logService, dataService);

			logService.AddEvent(
				new EventDataItem(
					EventCodes.EntryChanged, entry.Title,
					Utils.GetPermaLinkUrl(entry.EntryId)));
		}

		private static void InternalSaveEntry(Entry entry, string trackBackText, CrosspostInfoCollection crosspostList, SiteConfig siteConfig, ILoggingDataService logService, IBlogDataService dataService)
		{
			// we want to prepopulate the cross post collection with the crosspost footer
			if (siteConfig.EnableCrossPostFooter && siteConfig.CrossPostFooter != null && siteConfig.CrossPostFooter.Length > 0)
			{
				foreach (CrosspostInfo info in crosspostList)
				{
					info.CrossPostFooter = siteConfig.CrossPostFooter;
				}
			}

			// now save the entry, passign in all the necessary Trackback and Pingback info.
			try
			{
				dataService.SaveEntry(
					entry,
					(siteConfig.NotifyWebLogsDotCom || siteConfig.NotifyBloGs) ?
						new WeblogUpdatePingInfo(siteConfig.Title, Utils.GetBaseUrl(siteConfig), Utils.GetBaseUrl(siteConfig), Utils.GetRssUrl(siteConfig), siteConfig.NotifyWebLogsDotCom, siteConfig.NotifyBloGs) : null,
					(trackBackText != null && trackBackText.Length > 0 && entry.IsPublic) ?
						new TrackbackInfo(
							trackBackText,
							Utils.GetPermaLinkUrl( siteConfig, entry ),
							entry.Title,
							entry.Description,
							siteConfig.Title) : null,
					siteConfig.EnableAutoPingback && entry.IsPublic ?
						new PingbackInfo(
							Utils.GetPermaLinkUrl( siteConfig, entry ),
							entry.Title,
							entry.Description,
							siteConfig.Title) : null,
					crosspostList);

				SendEmail(entry, siteConfig, logService);
			}
			catch (Exception ex)
			{
				StackTrace st = new StackTrace();
				logService.AddEvent(
					new EventDataItem(EventCodes.Error, ex.ToString() + Environment.NewLine + st.ToString(), ""));
			}

			// we want to invalidate all the caches so users get the new post
			BreakCache(siteConfig, entry.GetSplitCategories());

			// give the XSS upstreamer a hint that things have changed
			XSSUpstreamer.TriggerUpstreaming();
		}

		private static void BreakCache(SiteConfig siteConfig, string[] categories)
		{
			// break the caching
			HttpRuntime.Cache.Remove("BlogCoreData");
			HttpRuntime.Cache.Remove("Rss::" + siteConfig.RssDayCount.ToString() + ":" + siteConfig.RssEntryCount.ToString());
	
			foreach(string category in categories)
			{
				string CacheKey = "Rss:" + category + ":" + siteConfig.RssDayCount.ToString() + ":" + siteConfig.RssEntryCount.ToString();
				HttpRuntime.Cache.Remove(CacheKey);
			}
		}

		/// <summary>
		/// Send an email notification that an entry has been made.
		/// </summary>
		/// <param name="siteConfig">The page making the request.</param>
		/// <param name="entry">The entry being added.</param>
		internal static void SendEmail(Entry entry, SiteConfig siteConfig, ILoggingDataService logService)
		{
			if (siteConfig.SendPostsByEmail &&
				siteConfig.SmtpServer != null &&
				siteConfig.SmtpServer.Length > 0)
			{
				MailMessage emailMessage = new MailMessage();

				if (siteConfig.NotificationEMailAddress != null &&
					siteConfig.NotificationEMailAddress.Length > 0)
				{
					emailMessage.To = siteConfig.NotificationEMailAddress;
				}
				else
				{
					emailMessage.To = siteConfig.Contact;
				}

				emailMessage.Subject = String.Format("Weblog post by '{0}' on '{1}'", entry.Author, entry.Title);
				emailMessage.Body = String.Format("{0}\r\nPost page: {1}", entry.Content, Utils.GetPermaLinkUrl(siteConfig,entry));
				emailMessage.BodyFormat = MailFormat.Text;
				emailMessage.BodyEncoding = Encoding.UTF8;

				User entryAuthor = SiteSecurity.GetUser(entry.Author);

				//didn't work? try again with emailAddress...
				if (entryAuthor == null) 
				{
					entryAuthor = SiteSecurity.GetUserByEmail(entry.Author);
				}

				if (entryAuthor != null && entryAuthor.EmailAddress != null && entryAuthor.EmailAddress.Length > 0)
				{
					emailMessage.From = entryAuthor.EmailAddress;
				}
				else
				{
					emailMessage.From = siteConfig.Contact;
				}

				SendMailInfo sendMailInfo = new SendMailInfo(emailMessage, siteConfig.SmtpServer,
					siteConfig.EnableSmtpAuthentication, siteConfig.SmtpUserName, siteConfig.SmtpPassword);

				try
				{
					// RyanG: Added support for authentication on outgoing smtp
					if (sendMailInfo.EnableSmtpAuthentication) 
					{
						sendMailInfo.Message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1");	// enable basic authentication
						sendMailInfo.Message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", sendMailInfo.SmtpUserName);	// set the username
						sendMailInfo.Message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", sendMailInfo.SmtpPassword);  // set the password
					}

					SmtpMail.SmtpServer = sendMailInfo.SmtpServer;
					SmtpMail.Send(sendMailInfo.Message);
				}
				catch (Exception ex)
				{
					logService.AddEvent(
						new EventDataItem(EventCodes.SmtpError, "", ex.Message));
				}
			}
		}

	}
}